package organizer.hb;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.DerbyTenSevenDialect;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import organizer.jdbc.JdbcUtil;

/**
 *
 * @author dawong
 */
public enum HibernateUtil {
    
    INSTANCE("jdbc:derby:" + dbDir()),
    TEST("jdbc:derby:memory:organizer");
    
    private static final String DB_NAME = "hibernate_derby";
    
    private Logger log = Logger.getLogger(HibernateUtil.class);
    private String url;
    private SessionFactory sessionFactory;
    private Configuration cfg;

    private HibernateUtil(String url) {
        this.url = url;
    }
    
    private static String dbDir() {
        String homePath = System.getProperty("user.home");
        File homeDir = new File(homePath, ".organizer");
        File dbDir = new File(homeDir, "hb");
        if (!dbDir.exists()) {
            dbDir.mkdirs();
        }
        String dbPath = dbDir.getAbsolutePath();
        return new File(dbPath, DB_NAME).getAbsolutePath();
    }

    public SessionFactory getSessionFactory() {
        if (sessionFactory == null) {
            sessionFactory = createSessionFactory();
        }
        return sessionFactory;
    }
    
    private Configuration getConfiguration() {
        if (cfg == null) {
            cfg = createConfiguration(false);
        }
        return cfg;
    }
    
    private Configuration createConfiguration(boolean create) {
        Properties props = new Properties();
        props.setProperty(Environment.DIALECT, DerbyTenSevenDialect.class.getName());
        if (create)
            props.setProperty(Environment.URL, url + ";create=true");
        else
            props.setProperty(Environment.URL, url);
        props.setProperty(Environment.USER, "ROOT");
        props.setProperty(Environment.PASS, "");
        props.setProperty(Environment.C3P0_MIN_SIZE, "1");
        props.setProperty(Environment.C3P0_MAX_SIZE, "3");
        props.setProperty(Environment.C3P0_TIMEOUT, "1800");
        props.setProperty(Environment.C3P0_MAX_STATEMENTS, "50");
        props.setProperty(Environment.DRIVER, "org.apache.derby.jdbc.EmbeddedDriver");
        props.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
        if (create)
            props.setProperty(Environment.HBM2DDL_AUTO, "create");
        else
            props.setProperty(Environment.HBM2DDL_AUTO, "update");
        Configuration config = new Configuration()
                .setProperties(props)
                .addAnnotatedClass(FileMetadataEntity.class);
        return config;
    }
    
    private SessionFactory createSessionFactory() {
        Configuration config = getConfiguration();
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(config.getProperties())
                .buildServiceRegistry();
        return config.buildSessionFactory(serviceRegistry);
    }
    
    private boolean databaseExists() {
        return new File(dbDir()).exists();
    }
    
    public void createDatabase() {
        if (!databaseExists() || this == HibernateUtil.TEST) {
            log.debug("creating database");
            Configuration config = createConfiguration(true);
            ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                    .applySettings(config.getProperties())
                    .buildServiceRegistry();
            config.buildSessionFactory(serviceRegistry);
            log.debug("finished creating database");
        }
    }
    
    /**
     * This is a workaround derby initialization problems.  Derby requires
     * the url contain ";create=true" when creating the database.
     */
    private void createDerbyDatabase() {
        String url = this.url + ";create=true";
        Connection c = null;
        try {
            c = DriverManager.getConnection(url, "ROOT", "");
        } catch (SQLException e) {
            log.error(null, e);
        } finally {
            if (c != null)
                try { c.close(); } catch (SQLException e) { log.error(null, e); }
        }
    }
    
    public void exportSchema() {
        log.debug("exporting schema: " + url);
        Configuration config = createConfiguration(false);
        new SchemaExport(config).create(false, true);
        log.debug("finished export");
    }
    
}
